import os
import openai
from dashscope.audio.tts_v2 import SpeechSynthesizer
import json
import re
import time
import traceback
import dashscope
from moviepy.editor import concatenate_audioclips, AudioFileClip
from typing import List
from utils import create_directory, save_file, read_text_from_file,load_config

project_config = load_config("config.json")

client = openai.OpenAI(
    api_key=os.getenv("DASHSCOPE_API_KEY"),
    base_url="https://dashscope.aliyuncs.com/compatible-mode/v1",
)

def create_speech_script(course_script):
    """
    创建演讲稿脚本，从给定的课程脚本中提取标题和内容。

    该函数将输入的课程脚本按行拆分，并提取其中的标题和内容，
    形成一个结构化的输出，包含标题及其对应的内容（去除 Markdown 格式）。

    参数:
    course_script (str): 包含课程内容的 Markdown 格式字符串。

    返回:
    str: 返回一个 JSON 格式的字符串，包含所有标题和内容。
    """
    output = []  # 用于保存输出的最终结果
    lines = course_script.splitlines()  # 将脚本按行拆分
    current_title = None  # 当前标题
    current_content = []  # 当前内容列表

    for line in lines:
        line = line.strip()  # 去除每行的首尾空白字符

        # 处理图片标签，去除 Markdown 图片格式
        if line.startswith('!['):
            continue  # 跳过图片行，直接进入下一个循环

        # 处理一级标题
        if line.startswith('# '):  # 检测到一级标题
            if current_title:  # 如果已有标题，保存当前内容
                output.append({
                    "title": current_title,
                    "content": "\n".join(current_content).replace('*', '').replace('**', '')
                })
                current_content = []  # 重置内容
            current_title = line[2:]  # 获取当前标题的文本内容（跳过 '# '）

        # 处理二级标题
        elif line.startswith('## '):  # 检测到二级标题
            if current_title:  # 如果已有标题，保存当前内容
                output.append({
                    "title": current_title,
                    "content": "\n".join(current_content).replace('*', '').replace('**', '')
                })
                current_content = []  # 重置内容
            current_title = line[3:]  # 获取当前标题的文本内容（跳过 '## '）

        else:
            # 处理列表和其他 Markdown 内容，移除多余的符号
            clean_line = re.sub(r'\*\s*', '', line)  # 移除以 '*' 开头的内容
            clean_line = re.sub(r'#+\s*', '', clean_line)  # 移除 Markdown 标题格式
            clean_line = clean_line.strip()  # 去除多余的空白字符

            if clean_line:  # 确保不添加空行
                current_content.append(clean_line)  # 将清理后的内容添加到当前内容列表

    # 辐忘记添加最后一个标题与内容
    if current_title:
        output.append({
            "title": current_title,
            "content": "\n".join(current_content).replace('*', '').replace('**', '')
        })

    # 将输出转换为 JSON 格式并返回
    generated_content = json.dumps(output, ensure_ascii=False, indent=4)

    # 打印生成的录音稿初稿
    print("生成的录音稿初稿：" + generated_content)

    return generated_content

def improve_speech_script(script):
    """
    优化录音稿的内容，使其更适合口语表达。

    此函数调用外部 API，对输入的录音稿 JSON 格式的内容进行处理，
    生成更适合发言的纯文本内容。

    参数:
    script (str): JSON 格式的字符串，包含待优化的内容。

    返回:
    str: 生成的优化后的口语化文本。
    """

    # 系统消息，设定机器人的角色
    system_message = "您是录音稿专家。"

    # 提示词创建，构建用于 API 请求的 prompt 字符串
    prompt = (
        f"处理以下 JSON 中的 content 字段，并将内容转换为适合录音的纯文本形式。"
        f"返回处理后的 JSON，不要任何额外的说明。内容格式要求：\n"
        "1. 对于英文的专有术语缩写，替换为全称。\n"  # 英文缩写的全称替换
        "2. 去除星号、井号等 Markdown 格式。\n"  # 移除 Markdown 标记
        "3. 去除换行符和段落分隔。\n"  # 删除换行符和段落分割符
        "4. 对于复杂的长难句，使用中文句号分割，便于口语表达。\n"  # 处理长难句使其更流畅
        " content 中的内容使用于发言使用。\n"  # 明确内容用途
        f"{script} "  # 添加待处理的 JSON 内容
        "输出格式为 JSON。不包含任何额外的文字、解释或评论。"  # 指明输出格式
    )

    # 调用 API 获取插图数据
    # 使用预设的系统消息和用户提示词来生成回复
    completion = client.chat.completions.create(
        model="qwen-max-latest",  # 指定使用的模型
        messages=[
            {"role": "system", "content": system_message},  # 系统角色消息
            {"role": "user", "content": prompt},  # 用户请求部分
        ],
    )

    # 解析 API 返回的 JSON 格式结果
    dumped_json = json.loads(completion.model_dump_json())  # 从响应中加载 JSON 数据

    # 返回生成的内容，提取优化后的文本
    generated_content = dumped_json['choices'][0]['message']['content']

    # 打印生成的优化后的录音稿
    print("生成的优化后的录音稿：" + generated_content)

    return generated_content  # 返回优化后的文本

# 定义一个 read_json_file 函数，用于从指定路径读取 JSON 文件并返回内容。
def read_json_file(file_path):
    with open(file_path, 'r', encoding='utf-8') as file:
        data = json.load(file)
    return data

# 定义一个 split_into_sentences 函数，用于将输入文本按中文标点和括号分割成句子。
def split_into_sentences(text):
    # 中文标点符号列表
    punctuation = ['，', '。', '；', '？', '！']
    brackets = {'(': ')', '[': ']', '{': '}', '（': '）', '【': '】', '《': '》'}

    # 初始化结果列表和临时句子存储
    sentences = []
    temp_sentence = ''
    bracket_stack = []

    # 遍历文本中的每一个字符
    for char in text:
        # 如果是左括号，压入栈
        if char in brackets:
            bracket_stack.append(char)
        # 如果是右括号且与栈顶匹配，弹出栈
        elif char in brackets.values() and bracket_stack and brackets[bracket_stack[-1]] == char:
            bracket_stack.pop()

        # 如果字符是中文标点之一且括号栈为空，表示句子结束
        if char in punctuation and not bracket_stack:
            # 添加临时句子到结果列表，并清空临时句子
            sentences.append(temp_sentence.strip())
            temp_sentence = ''
        # 如果字符是空格，也可以视为句子结束
        elif char == ' ':
            # 如果临时句子不是空，将其添加到结果列表
            if temp_sentence.strip():  # 仅在临时句子不为空时添加
                sentences.append(temp_sentence.strip())
                temp_sentence = ''
        else:
            # 否则，将字符添加到临时句子中
            temp_sentence += char

    # 处理最后一个可能没有标点结尾的句子
    if temp_sentence:
        sentences.append(temp_sentence.strip())

    return sentences

# 定义一个 save_sentences_to_markdown 函数，用于将分割后的句子保存为 Markdown 文件。
def save_sentences_to_markdown(sentences, base_dir, index1):
    for index2, sentence in enumerate(sentences, start=1):
        # 创建目录
        dir_name = f'audio_for_paragraph_{index1}'
        dir_path = os.path.join(base_dir, dir_name)
        os.makedirs(dir_path, exist_ok=True)

        # 构建文件名
        file_name = f'paragraph_{index1}_sentence_{index2}.md'
        file_path = os.path.join(dir_path, file_name)

        # 写入Markdown文件
        with open(file_path, 'w', encoding='utf-8') as file:
            file.write(sentence + '\n')

# 定义一个 process_json_file 函数，用于处理指定的 JSON 文件并生成 Markdown 文件。
def process_json_file(json_file_path, base_dir):

    if not os.path.exists(base_dir):
        os.makedirs(base_dir)

    file_prefix = os.path.splitext(os.path.basename(json_file_path))[0]

    # base_dir = os.path.join(base_dir, file_prefix)

    # 读取JSON文件
    json_data = read_json_file(json_file_path)

    # 处理JSON数据中的每个条目
    for index1, item in enumerate(json_data):
        if 'content' in item:
            content = item['content']
            # 检查content是否为链接
            if not is_url(content):
                sentences = split_into_sentences(content)
                save_sentences_to_markdown(sentences, base_dir, index1+1)

def is_url(s):
    url_pattern = re.compile(r'http[s]?://(?:[a-zA-Z]|[0-9]|[$-_@.&+]|[!*\\(\\),]|(?:%[0-9a-fA-F][0-9a-fA-F]))+')
    return bool(url_pattern.match(s))


def synthesize_md_to_speech(base_directory):
    """
    识别指定目录下的所有.md文件，读取其内容并使用DashScope API将其转换为语音，
    保存为同名.mp3文件在同一目录下。

    参数:
    base_directory (str): 包含.md文件的顶层目录路径。
    """
    # 确保环境变量中存在DashScope API密钥
    if 'DASHSCOPE_API_KEY' not in os.environ:
        raise ValueError("DashScope API key must be set in the environment variables.")

    # 遍历指定目录及其子目录
    for root, dirs, files in os.walk(base_directory):
        for file in files:
            if file.endswith('.md'):
                # 构建完整文件路径
                md_file_path = os.path.join(root, file)

                # 读取.md文件内容
                with open(md_file_path, 'r', encoding='utf-8') as f:
                    text = f.read()

                # 初始化语音合成器
                speech_synthesizer = SpeechSynthesizer(model='cosyvoice-v1', voice='longxiaochun')


                # 合成语音
                audio_data = speech_synthesizer.call(text)

                # 构建输出.mp3文件路径
                mp3_file_path = os.path.splitext(md_file_path)[0] + '.mp3'

                # 保存音频到文件
                with open(mp3_file_path, 'wb') as f:
                    f.write(audio_data)

                print(f'Synthesized text from file "{md_file_path}" to file: {mp3_file_path}')

def format_time(seconds):
    hours, remainder = divmod(seconds, 3600)
    minutes, seconds = divmod(remainder, 60)
    milliseconds = int((seconds - int(seconds)) * 1000)
    seconds = int(seconds)
    return f"{int(hours):02d}:{int(minutes):02d}:{int(seconds):02d},{milliseconds:03d}"

def get_audio_duration(file_path):
    audio = AudioFileClip(file_path)
    duration = audio.duration
    audio.close()
    return duration

def create_srt_line(index, start_time, end_time, text):
    return f"{index}\n{start_time} --> {end_time}\n{text}\n\n"

# 定义一个 generate_srt_from_audio 函数，用于生成字幕。
def generate_srt_from_audio(base_dir: str, output_dir: str, output_srt_file: str) -> None:
    """
    从指定目录下的音频文件夹生成SRT字幕文件。

    :param base_dir: 包含音频文件夹的根目录。
    :param output_dir: 输出SRT文件的目录。
    :param output_srt_file: 输出SRT文件的完整路径。
    """

    # 创建输出目录，如果它不存在
    if not os.path.exists(output_dir):
        os.makedirs(output_dir)

    # 确保输出文件名有.srt后缀
    if not output_srt_file.endswith('.srt'):
        output_srt_file += '.srt'


    # 初始化当前时间
    current_time = 2.000  # 初始时间

    # 打开SRT文件进行写入
    with open(output_srt_file, 'w', encoding='utf-8') as srt_file:
        srt_index = 1

        # 获取所有符合条件的子目录，并按索引排序
        sub_dirs = [d for d in os.listdir(base_dir) if d.startswith('audio_for_paragraph_')]
        sub_dirs.sort(key=lambda x: int(re.search(r'\d+', x).group()))

        # 遍历所有子目录
        for sub_dir in sub_dirs:
            sub_dir_path = os.path.join(base_dir, sub_dir)

            # 查找所有的.md和.mp3文件
            files = [f for f in os.listdir(sub_dir_path) if f.endswith('.md') or f.endswith('.mp3')]
            md_files = [f for f in files if f.endswith('.md')]

            # 按照index1和index2排序.md文件
            md_files.sort(key=lambda x: (int(x.split('_')[1]), int(x.split('_')[3].split('.')[0])))

            # 处理每个.md文件
            for md_file in md_files:
                md_file_path = os.path.join(sub_dir_path, md_file)
                mp3_file_path = os.path.splitext(md_file_path)[0] + '.mp3'

                # 确保对应的.mp3文件存在
                if os.path.exists(mp3_file_path):
                    # 读取.md文件内容
                    with open(md_file_path, 'r', encoding='utf-8') as f:
                        text = f.read().strip()

                    # 获取.mp3文件时长
                    duration = get_audio_duration(mp3_file_path)

                    # 生成SRT格式的字幕行
                    start_time_str = format_time(current_time)
                    end_time_str = format_time(current_time + duration)
                    srt_line = create_srt_line(srt_index, start_time_str, end_time_str, text)

                    # 写入SRT文件
                    srt_file.write(srt_line)

                    # 更新当前时间
                    current_time += duration + 0.3  # 加上0.5秒以避免时间重叠

                    srt_index += 1
                else:
                    print(f"No corresponding MP3 file found for {md_file}")

    print("成功生成字幕文件：" + output_srt_file)
# 读取配置文件中的课程脚本文件路径
course_script_with_illustrations_file_path = project_config["course_script_with_illustrations_file_path"].format(title=project_config["title"])

# 根据课程脚本文件路径读取课程内容
script = read_text_from_file(course_script_with_illustrations_file_path)

# 调用函数生成录音稿初稿
speech_script_draft = create_speech_script(script)

# 读取配置文件中的录音稿初稿文件路径
speech_script_draft_file_path = project_config["speech_script_draft_file_path"].format(title=project_config["title"])

# 保存录音稿初稿文件
save_file(speech_script_draft, speech_script_draft_file_path)

# 调用函数优化录音稿初稿
improved_speech_script = improve_speech_script(speech_script_draft)

# 读取配置文件中的优化后的录音稿文件路径
speech_script_file_path = project_config["speech_script_file_path"].format(title=project_config["title"])

# 保存优化后的录音稿文件
save_file(improved_speech_script, speech_script_file_path)

# 读取配置文件中的音频文件所在目录
audio_file_folder = project_config["audio_file_folder"].format(title=project_config["title"])

# 切分录音稿
process_json_file(speech_script_file_path, audio_file_folder)

# 将录音稿片段转换为语音
synthesize_md_to_speech(audio_file_folder)

# 读取配置文件中的音频文件所在目录
audio_file_folder = project_config["audio_file_folder"].format(title=project_config["title"])

# 读取配置文件中的字幕文件所在目录
srt_file_folder = project_config["srt_file_folder"]

# 读取配置文件中的字幕文件路径
srt_file_path = project_config["srt_file_path"].format(title=project_config["title"])

# 生成 SRT 文件
generate_srt_from_audio(audio_file_folder, srt_file_folder, srt_file_path)

